package lang.ref;


import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 细颗粒 hash 弱引用锁，自动清理机制不会内存泄漏
 * guava 的 Cache 估计已经实现了 to do 看一下原理
 **/
public class WeakHashLock<K> {
    private ConcurrentHashMap<K, WeakLockRef> lockMap = new ConcurrentHashMap<>(1000);
    private ReferenceQueue<ReentrantLock> queue = new ReferenceQueue<>();

    /**
     * 运行完美可用于生产
     *
     * @param args
     */
    public static void main(String[] args) throws Exception {
//      test1();
        test2();
    }

    private static void test2() throws InterruptedException {
        int threadNum = 200;
        CountDownLatch count = new CountDownLatch(threadNum);

        WeakHashMap<Integer, ReentrantLock> weakHashMap = new WeakHashMap<>();
        for (int i = 1; i <= threadNum; i++) {
            new Thread(() -> {
                try {
                    count.await();
                    ReentrantLock lock = new ReentrantLock();
                    weakHashMap.put(ThreadLocalRandom.current().nextInt(threadNum), lock);
                    lock.lock();
                    try {
                        System.out.println("get lock");
                    } finally {
                        lock.unlock();

                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }).start();

            count.countDown();
        }

        Thread.sleep(3000);
        System.gc();//方便测试
        System.out.println(weakHashMap.size());
    }

    public static void test1() {
        int threadNum = 200;
        CountDownLatch count = new CountDownLatch(threadNum);

        WeakHashLock<Integer> weakHashLock = new WeakHashLock<>();

        for (int i = 1; i <= threadNum; i++) {
            new Thread(() -> {
                try {
                    count.await();
                    ReentrantLock lock = weakHashLock.get(ThreadLocalRandom.current().nextInt(threadNum));
                    lock.lock();
                    try {
                        System.out.println("get lock");
                    } finally {
                        lock.unlock();
                        //System.gc();//方便测试
                    }
                } catch (InterruptedException e) {
                }

            }).start();

            count.countDown();
        }
    }

    public ReentrantLock get(K key) {
        Objects.requireNonNull(key, "key is null");

        WeakLockRef weakLockRef = lockMap.get(key);
        ReentrantLock lock = weakLockRef == null ? null : weakLockRef.get();
        if (lock != null) {
            return lock;
        }
        //手动触发清除 hash 里面无效的key
        clearEmptyRef();

        //不用 put 是因为 可能2个线程同时到这一步
        ReentrantLock newLock = new ReentrantLock();
        lockMap.put(key, new WeakLockRef(key, newLock, queue));
        return newLock;
    }

    /**
     * 回收 gc 掉的lock
     */
    @SuppressWarnings("all")
    private void clearEmptyRef() {
        Reference<? extends ReentrantLock> ref;
        while ((ref = queue.poll()) != null) {
            WeakLockRef weakLockRef = (WeakLockRef) ref;
            K key = (K) weakLockRef.key;
            lockMap.remove(key);            // to do log
            System.out.println(String.format("deleted key [%s]", key));
        }
    }

    private static final class WeakLockRef extends WeakReference<ReentrantLock> {
        final Object key;

        private WeakLockRef(Object key, ReentrantLock lock, ReferenceQueue<ReentrantLock> queue) {
            super(lock, queue);
            this.key = key;
        }
    }


}
